#include "unittest.h"
#include "base.h"
#include "scanner.h"
#include "context.h"
#include <string.h>
#include <stdlib.h>
#include <assert.h>


static void
test_punctuation(const_pointer nothing)
{
	byte* str = "{}[]():.,|::_;<< >> -> <-";
	struct LqContext* c = lq_context_new(str, "");

	assert(lq_scanner_next(c).value == LQ_TOKEN_CURVEDLEFTBRACE);
	assert(lq_scanner_next(c).value == LQ_TOKEN_CURVEDRIGHTBRACE);
	assert(lq_scanner_next(c).value == LQ_TOKEN_SQUARELEFTBRACE);
	assert(lq_scanner_next(c).value == LQ_TOKEN_SQUARERIGHTBRACE);
	assert(lq_scanner_next(c).value == LQ_TOKEN_ROUNDLEFTBRACE);
	assert(lq_scanner_next(c).value == LQ_TOKEN_ROUNDRIGHTBRACE);
	assert(lq_scanner_next(c).value == LQ_TOKEN_COLON);
	assert(lq_scanner_next(c).value == LQ_TOKEN_DOT);
	assert(lq_scanner_next(c).value == LQ_TOKEN_COMMA);
	assert(lq_scanner_next(c).value == LQ_TOKEN_PIPE);
	assert(lq_scanner_next(c).value == LQ_TOKEN_DOUBLECOLON);
	assert(lq_scanner_next(c).value == LQ_TOKEN_UNDERSCORE);
	assert(lq_scanner_next(c).value == LQ_TOKEN_SEMICOLON);
	assert(lq_scanner_next(c).value == LQ_TOKEN_ARRAYLEFTBRACE);
	assert(lq_scanner_next(c).value == LQ_TOKEN_ARRAYRIGHTBRACE);
	assert(lq_scanner_next(c).value == LQ_TOKEN_ARROWRIGHT);
	assert(lq_scanner_next(c).value == LQ_TOKEN_ARROWLEFT);
	assert(lq_scanner_next(c).value == LQ_TOKEN_EOF);

	lq_context_free(c);
}



static void
test_operator(const_pointer nothing)
{
        byte* str = "+ - / * mod ^ ++ -- >= =< < > == = <> <+ +> !! ! & >< ~";
	struct LqContext* c = lq_context_new(str, "");

	assert(lq_scanner_next(c).value == LQ_TOKEN_PLUS);
	assert(lq_scanner_next(c).value == LQ_TOKEN_MINUS);
	assert(lq_scanner_next(c).value == LQ_TOKEN_DIVIDE);
	assert(lq_scanner_next(c).value == LQ_TOKEN_MULTIPLY);
	assert(lq_scanner_next(c).value == LQ_TOKEN_MODULO);
	assert(lq_scanner_next(c).value == LQ_TOKEN_POWER);
	assert(lq_scanner_next(c).value == LQ_TOKEN_CONCAT);
	assert(lq_scanner_next(c).value == LQ_TOKEN_REDUCE);
	assert(lq_scanner_next(c).value == LQ_TOKEN_GREATEREQUAL);
	assert(lq_scanner_next(c).value == LQ_TOKEN_LESSEQUAL);
	assert(lq_scanner_next(c).value == LQ_TOKEN_LESS);
	assert(lq_scanner_next(c).value == LQ_TOKEN_GREATER);
	assert(lq_scanner_next(c).value == LQ_TOKEN_EQUAL);
	assert(lq_scanner_next(c).value == LQ_TOKEN_ASSIGN);
	assert(lq_scanner_next(c).value == LQ_TOKEN_NOTEQUAL);
	assert(lq_scanner_next(c).value == LQ_TOKEN_BITSHIFTLEFT);
	assert(lq_scanner_next(c).value == LQ_TOKEN_BITSHIFTRIGHT);
	assert(lq_scanner_next(c).value == LQ_TOKEN_DOUBLEEXCLAMATION);
	assert(lq_scanner_next(c).value == LQ_TOKEN_EXCLAMATION);
	assert(lq_scanner_next(c).value == LQ_TOKEN_BAND);
	assert(lq_scanner_next(c).value == LQ_TOKEN_BXOR);
	assert(lq_scanner_next(c).value == LQ_TOKEN_BNOT);
	assert(lq_scanner_next(c).value == LQ_TOKEN_EOF);

	lq_context_free(c);
}



static void
test_integer(const_pointer nothing)
{
        byte* str = " 0 123 0xFF 2x10 8x777 1_000 0xFF_FF 2x1111_1111 8x777_777";
	struct LqContext* c = lq_context_new(str, "");
        struct LqToken tok;

        tok = lq_scanner_next(c);
        assert(tok.value == LQ_TOKEN_DECNUMBER);
        assert(strcmp(tok.text, "0") == 0);
        free(tok.text);

        tok = lq_scanner_next(c);
        assert(tok.value == LQ_TOKEN_DECNUMBER);
        assert(strcmp(tok.text, "123") == 0);
        free(tok.text);

        tok = lq_scanner_next(c);
        assert(tok.value == LQ_TOKEN_HEXNUMBER);
        assert(strcmp(tok.text, "FF") == 0);
        free(tok.text);

        tok = lq_scanner_next(c);
        assert(tok.value == LQ_TOKEN_BINNUMBER);
        assert(strcmp(tok.text, "10") == 0);
        free(tok.text);

        tok = lq_scanner_next(c);
        assert(tok.value == LQ_TOKEN_OCTNUMBER);
        assert(strcmp(tok.text, "777") == 0);
        free(tok.text);

        tok = lq_scanner_next(c);
        assert(tok.value == LQ_TOKEN_DECNUMBER);
        assert(strcmp(tok.text, "1000") == 0);
        free(tok.text);

        tok = lq_scanner_next(c);
        assert(tok.value == LQ_TOKEN_HEXNUMBER);
        assert(strcmp(tok.text, "FFFF") == 0);
        free(tok.text);

        tok = lq_scanner_next(c);
        assert(tok.value == LQ_TOKEN_BINNUMBER);
        assert(strcmp(tok.text, "11111111") == 0);
        free(tok.text);

        tok = lq_scanner_next(c);
        assert(tok.value == LQ_TOKEN_OCTNUMBER);
        assert(strcmp(tok.text, "777777") == 0);
        free(tok.text);

	assert(lq_scanner_next(c).value == LQ_TOKEN_EOF);

	lq_context_free(c);
}


static void
test_floatpoint(const_pointer nothing)
{
        byte* str = " 0.0 5.5 10e2 0.5e-3 1e+3 1e0";
	struct LqContext* c = lq_context_new(str, "");
        struct LqToken tok;

        tok = lq_scanner_next(c);
        assert(tok.value == LQ_TOKEN_FLOATPOINT);
        assert(strcmp(tok.text, "0.0") == 0);
        free(tok.text);

        tok = lq_scanner_next(c);
        assert(tok.value == LQ_TOKEN_FLOATPOINT);
        assert(strcmp(tok.text, "5.5") == 0);
        free(tok.text);

        tok = lq_scanner_next(c);
        assert(tok.value == LQ_TOKEN_FLOATPOINT);
        assert(strcmp(tok.text, "10e2") == 0);
        free(tok.text);

        tok = lq_scanner_next(c);
        assert(tok.value == LQ_TOKEN_FLOATPOINT);
        assert(strcmp(tok.text, "0.5e-3") == 0);
        free(tok.text);

        tok = lq_scanner_next(c);
        assert(tok.value == LQ_TOKEN_FLOATPOINT);
        assert(strcmp(tok.text, "1e+3") == 0);
        free(tok.text);

        tok = lq_scanner_next(c);
        assert(tok.value == LQ_TOKEN_FLOATPOINT);
        assert(strcmp(tok.text, "1e0") == 0);
        free(tok.text);

	assert(lq_scanner_next(c).value == LQ_TOKEN_EOF);

	lq_context_free(c);
}



static void
test_string(const_pointer nothing)
{
        byte* str = " \"asdf\" \"test\\uFFFF\\UFFFFFFFF\\n\" ";
	struct LqContext* c = lq_context_new(str, "");
        struct LqToken tok;

        tok = lq_scanner_next(c);
        assert(tok.value == LQ_TOKEN_STRING);
        assert(strcmp(tok.text, "asdf") == 0);
        free(tok.text);

        tok = lq_scanner_next(c);
        assert(tok.value == LQ_TOKEN_STRING);
        assert(strcmp(tok.text, "test\\uFFFF\\UFFFFFFFF\\n") == 0);
        free(tok.text);

	assert(lq_scanner_next(c).value == LQ_TOKEN_EOF);

	lq_context_free(c);
}



static void
test_rawstring(const_pointer nothing)
{
        byte* str = " '(a|b)*' ";
	struct LqContext* c = lq_context_new(str, "");
        struct LqToken tok;

        tok = lq_scanner_next(c);
        assert(tok.value == LQ_TOKEN_RAWSTRING);
        assert(strcmp(tok.text, "(a|b)*") == 0);
        free(tok.text);

	assert(lq_scanner_next(c).value == LQ_TOKEN_EOF);

	lq_context_free(c);
}



static void
test_identifier(const_pointer nothing)
{
        byte* str = " OK Pi exists? id";
	struct LqContext* c = lq_context_new(str, "");
        struct LqToken tok;

        tok = lq_scanner_next(c);
        assert(tok.value == LQ_TOKEN_ATOM);
        assert(strcmp(tok.text, "OK") == 0);
        free(tok.text);
 
        tok = lq_scanner_next(c);
        assert(tok.value == LQ_TOKEN_IDENTIFIER);
        assert(strcmp(tok.text, "Pi") == 0);
        free(tok.text);
        
        tok = lq_scanner_next(c);
        assert(tok.value == LQ_TOKEN_EXTIDENTIFIER);
        assert(strcmp(tok.text, "exists?") == 0);
        free(tok.text);

        tok = lq_scanner_next(c);
        assert(tok.value == LQ_TOKEN_IDENTIFIER);
        assert(strcmp(tok.text, "id") == 0);
        free(tok.text);

	assert(lq_scanner_next(c).value == LQ_TOKEN_EOF);

	lq_context_free(c);
}



static void
test_keyword(const_pointer nothing)
{
        byte* str = " if then else match by fun let ";
	struct LqContext* c = lq_context_new(str, "");

        assert(lq_scanner_next(c).value == LQ_TOKEN_IF);
        assert(lq_scanner_next(c).value == LQ_TOKEN_THEN);
        assert(lq_scanner_next(c).value == LQ_TOKEN_ELSE);
        assert(lq_scanner_next(c).value == LQ_TOKEN_MATCH);
        assert(lq_scanner_next(c).value == LQ_TOKEN_BY);
        assert(lq_scanner_next(c).value == LQ_TOKEN_FUN);
        assert(lq_scanner_next(c).value == LQ_TOKEN_LET);
        assert(lq_scanner_next(c).value == LQ_TOKEN_EOF);

        lq_context_free(c);
}



static void
test_character(const_pointer nothing)
{
        byte* str = " \\a ";
	struct LqContext* c = lq_context_new(str, "");
        struct LqToken tok;

        tok = lq_scanner_next(c);
        assert(tok.value == LQ_TOKEN_CHARACTER);
        assert(strcmp(tok.text, "a") == 0);
        free(tok.text);
        
        assert(lq_scanner_next(c).value == LQ_TOKEN_EOF);

        lq_context_free(c);
}



void 
lq_test_scanner(void)
{
        lq_unittest_run("scanner.punctuation", test_punctuation, 0, 10);
        lq_unittest_run("scanner.operator", test_operator, 0, 10);
        lq_unittest_run("scanner.integer", test_integer, 0, 10);
        lq_unittest_run("scanner.character", test_character, 0, 10);
        lq_unittest_run("scanner.floatpoint", test_floatpoint, 0, 10);
        lq_unittest_run("scanner.string", test_string, 0, 10);
        lq_unittest_run("scanner.rawstring", test_rawstring, 0, 10);
        lq_unittest_run("scanner.identifier", test_identifier, 0, 10);
        lq_unittest_run("scanner.keyword", test_keyword, 0, 10);
        lq_unittest_run("scanner.character", test_character, 0, 10);
}

